import pymysql
import networkx as nx
from collections import Counter
import random

cnn = pymysql.connect(host='127.0.0.1', user='root', password='123456', port=3306, database='flask_douban_comment',
                      charset='utf8')

def build_graph():
    G = nx.Graph()
    cursor = cnn.cursor()
    sql = 'select uid, iid from tb_rate'
    cursor.execute(sql)
    for uid, iid in cursor.fetchall():
        G.add_node(f'u{uid}', bipartite=0)
        G.add_node(f'i{iid}', bipartite=1)
        G.add_edge(f'u{uid}', f'i{iid}')
    cursor.close()
    return G

def get_user_communities():
    """基于电影ID范围划分用户社群"""
    cursor = cnn.cursor()
    
    # 获取每个用户评分的电影ID范围
    sql = '''
    SELECT uid, 
           MIN(iid) as min_movie_id,
           MAX(iid) as max_movie_id,
           AVG(iid) as avg_movie_id
    FROM tb_rate 
    GROUP BY uid
    '''
    cursor.execute(sql)
    user_movie_ranges = cursor.fetchall()
    
    # 基于电影ID范围划分社群
    community1_users = []  # 电影ID <= 3000的用户
    community2_users = []  # 电影ID > 3000的用户
    
    for uid, min_id, max_id, avg_id in user_movie_ranges:
        # 如果用户主要评分电影ID <= 3000，归入社群1
        if avg_id <= 3000:
            community1_users.append(uid)
        else:
            community2_users.append(uid)
    
    cursor.close()
    
    print(f"社群1用户数: {len(community1_users)}, 社群2用户数: {len(community2_users)}")
    
    return {
        'community1': community1_users,
        'community2': community2_users
    }

def recommend(userId, top_n=5):
    G = build_graph()
    user_node = f'u{userId}'
    if user_node not in G:
        return []
    
    # 使用新的社群划分方式
    communities = get_user_communities()
    
    # 找到用户所属的社群
    user_community = None
    if userId in communities['community1']:
        user_community = communities['community1']
        print(f"用户{userId}属于社群1（电影ID<=3000）")
    elif userId in communities['community2']:
        user_community = communities['community2']
        print(f"用户{userId}属于社群2（电影ID>3000）")
    else:
        print(f"用户{userId}未找到所属社群")
        return []
    
    # 找到同社群的其他用户
    community_users = [uid for uid in user_community if uid != userId]
    print(f"同社群其他用户数量: {len(community_users)}")
    
    if not community_users:
        # 兜底：推荐全局热门电影
        print("同社群无其他用户，使用全局热门电影兜底")
        cursor = cnn.cursor()
        cursor.execute('''
            SELECT iid, COUNT(*) as cnt 
            FROM tb_rate 
            GROUP BY iid 
            ORDER BY cnt DESC 
            LIMIT %s
        ''', (top_n,))
        recs = [(int(iid), int(cnt)) for iid, cnt in cursor.fetchall()]
        cursor.close()
        # 去重
        seen = set()
        unique_result = []
        for mid, score in recs:
            if mid not in seen:
                unique_result.append((mid, score))
                seen.add(mid)
            if len(unique_result) >= top_n:
                break
        return unique_result
    
    # 统计这些用户喜欢的电影
    movie_counter = Counter()
    cursor = cnn.cursor()
    for uid in community_users:
        sql = 'SELECT iid FROM tb_rate WHERE uid = %s'
        cursor.execute(sql, (uid,))
        for (iid,) in cursor.fetchall():
            movie_counter[iid] += 1
    cursor.close()
    
    # 排除目标用户已看过的
    watched = set()
    cursor = cnn.cursor()
    cursor.execute('SELECT iid FROM tb_rate WHERE uid = %s', (userId,))
    for (iid,) in cursor.fetchall():
        watched.add(iid)
    cursor.close()
    
    recs = [(int(iid), count) for iid, count in movie_counter.items() if iid not in watched]
    print(f"排除已看过的电影后候选数量: {len(recs)}")
    
    # 添加随机性：从候选集中随机选择
    if len(recs) > top_n:
        # 按权重随机选择，权重越高的电影被选中的概率越大
        weights = [count for _, count in recs]
        selected_indices = random.choices(range(len(recs)), weights=weights, k=top_n*2)  # 多抽几倍
        # 去重
        seen = set()
        unique_result = []
        for idx in selected_indices:
            mid, score = recs[idx]
            if mid not in seen:
                unique_result.append((mid, score))
                seen.add(mid)
            if len(unique_result) >= top_n:
                break
        recs = unique_result
    else:
        # 去重
        seen = set()
        unique_result = []
        for mid, score in recs:
            if mid not in seen:
                unique_result.append((mid, score))
                seen.add(mid)
            if len(unique_result) >= top_n:
                break
        recs = unique_result
    
    return recs 